package main

import "fmt"

func main() {
	// nilSlice()
	// nilMap()
	// nilChan()
	nilInterface()
}

/** output
[] true
[] false
*/
func nilSlice() {
	var c []int
	var c2 = make([]int, 0)
	fmt.Println(c, c == nil)   //var 只是声明变量，并未分配内存，底层 slice 结构体 指向数组的指针字段未初始化即为0，与 nil 比较 为 true
	fmt.Println(c2, c2 == nil) // make 会初始化内存，c2 变量指向的 底层 slice data 指针 不等于 nil
}

/** output
map[] true
map[] false
*/
func nilMap() {
	var c map[string]struct{}
	var c2 = make(map[string]struct{})
	fmt.Println(c, c == nil)   // true，只是声明了一个c变量，1个占用8字节的指针，指向底层的 hmap 结构，但是var关键字并不会进行底层结构初始化，即c指向"空地址"
	fmt.Println(c2, c2 == nil) // false，使用 make 始始化 map，底层会调用 makemap 函数对 map 进行初始化
}

//同 nilMap 解释
/** output
<nil> true
0xc000102060 false
*/
func nilChan() {
	var c chan struct{}
	var c2 = make(chan struct{})
	fmt.Println(c, c == nil)   //true
	fmt.Println(c2, c2 == nil) //false
}

/**
[==比较总结](https://darjun.github.io/2019/08/20/golang-equal/)
1. 非接口类型，进行 == 操作的前提是两个值类型 必须相同
2. 引用类型进行比较，是指两个变量是不是指向同一分数据
3. 接口类型，会自动转换 类型，如果两个类型间不能转换，则无法比较。转换后再比较接口值
4. 接口类型与nil比较，只有动态类型与动态值都为 nil 时，该接口变量与 nil 比较结果为 true
*/
func nilInterface() {
	/*	type myInt8 int8
		var (
			a int8
			b myInt8
		)
		//a 底层类型是 int8 与 b 底层类型 是自定义类型 myInt8，类型元数据不相同，进行 == 比较报编译错误
		fmt.Println(a == b)//编译不通过
	*/
	type t interface{} // 自定义类型 t
	var (
		t1    t                //eface._type 动态类型为 nil ，data 为 nil
		t2    *t               //eface._type 动态类型为 *main.t , eface.data 为 nil
		t3    interface{} = t1 //t3 底层接口的 eface._type 动态类型为 t1，即 nil，动态值(eface.data)为 nil
		t4    interface{} = t2 //t4 底层接口的 动态类型为 t2 即 *main.t，动态值(eface.data)就是 nil
		slice []int
		s1    interface{} = slice   // s1 是空接口类型，将 变量 slice 赋值给 空接口。s1的动态类型为 []int，动态值 为[] 空数组
		s2    interface{} = []int{} // s2 是空接口类型，赋值操作后，s2 底层 eface._type 对应动态类型为 []int，动态值为 [] 空数组
	)
	fmt.Printf("t1: %T,%v \n", t1, t1)
	fmt.Printf("t2: %T,%v \n", t2, t2)
	fmt.Printf("t3: %T,%v \n", t3, t3)
	fmt.Printf("t4: %T,%v \n", t4, t4)
	fmt.Printf("s1: %T,%v \n", s1, s1)
	fmt.Printf("s2: %T,%v \n", s2, s2)
	fmt.Printf("slice: %T,%v \n", slice, slice)
	fmt.Println(t3 == t1, t1 == nil, t3 == nil) // true true true
	/**
	t4 == t2 ，比较时会将 t2 转换为 t4，转换后t2动态类型为 t*，t2 的动态值也为 t2
	t4 == nil，t4 的动态类型为 t*，动态值为 t2，是*t指针类型，故 与 nil 比较为 false
	t2 == nil，t2 只是通过 var 关键字声明的 一个 *t类型的变量，没有赋值操作，为 nil
	*/
	fmt.Println(t4 == t2, t4 == nil, t2 == nil) // true false true
	fmt.Println(s1 == nil)                      //false
	fmt.Println(s2 == nil)                      //false
	fmt.Println(slice == nil)                   //true, var 声明切片后，切片底层data数组字段未初始化为零值，所以与 nil 比较为 true。若用make初始化切片，则会分配data空间

}
